/*
 * mfc codec encoding example application
 * Andrzej Hajda <a.hajda@samsung.com>
 *
 * Input camera device.
 *
 * Copyright 2012 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h>
#include <poll.h>
#include <semaphore.h>

#include "common.h"
#include "in_demo.h"
#include "v4l_dev.h"
#include "args.h"

typedef struct
{
	void *start;
	int length;
}BUFTYPE;
#define ReqButNum 5

BUFTYPE *user_buf;
static int counter_limit;
static int counter;
static pthread_t camera_process_tid;
static int cam_fd;
static sem_t wait_post_sem;//wait to fill the buffer;
static sem_t wait_get_sem;
static int buf_index;


int get_image_buf(void **buf)
{
	sem_wait(&wait_get_sem);
	*buf = user_buf[buf_index].start;
	return user_buf[buf_index].length;
}

int post_image_buf()
{
	sem_post(&wait_post_sem);
	return 0;
}

static void *camera_process_thread(void *pvoid)
{
	struct pollfd pfds[1];
	struct v4l2_buffer buf;
	int ret;
	dbg("camera_process_thread");
	while(counter++ < counter_limit)
	{
		pfds[0].events = POLLIN | POLLPRI;
		pfds[0].fd = cam_fd;
		ret = poll(pfds, 1, -1);
		if(-1 == ret){
			if(EINTR == errno)
				continue;
			err("Fail to cam poll");
			return -1;
		}


		memzero(buf);
		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = V4L2_MEMORY_MMAP;
		if(ioctl(cam_fd, VIDIOC_DQBUF, &buf) < 0){
			err("Fail to dequeue buf");
			return -1;
		}
		buf_index = buf.index;		
		sem_post(&wait_get_sem);
		sem_wait(&wait_post_sem);
		if(ioctl(cam_fd, VIDIOC_QBUF, &buf) < 0){
			err("Fail to queue buf");
			return -1;
		}

	}
	return NULL;
}
int camera_init(struct options option)
{
	int fd;
	int i;
	struct v4l2_format stream_fmt;
	struct v4l2_fmtdesc fmtdesc;
	struct v4l2_streamparm stream;
	struct v4l2_requestbuffers reqbuf;
	enum v4l2_buf_type v4l2_buf_type;
	struct v4l2_buffer v4l2_buf;
	struct v4l2_capability cap;

	counter_limit = option.duration;
	counter = 0;
	
	sem_init(&wait_post_sem, 0, 0);
	sem_init(&wait_get_sem, 0, 0);
	
	if((fd = open(option.in_name, O_RDWR)) < 0){
		err("Fail to open camera");
		close(fd);
		return -1;
	}
	cam_fd = fd;

	memzero(stream_fmt);
	stream_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	stream_fmt.fmt.pix.width = option.in_width;
	stream_fmt.fmt.pix.height = option.in_height;
	stream_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
	stream_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
	if(ioctl(fd, VIDIOC_S_FMT, &stream_fmt) < 0){
		err("Fail to set camera format\n");
		close(fd);
		return -1;
	}

	memzero(reqbuf);
	reqbuf.count = ReqButNum;
	reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	reqbuf.memory = V4L2_MEMORY_MMAP;
	if(ioctl(fd, VIDIOC_REQBUFS, &reqbuf) < 0){
		err("Fail to request buffer'");
		close(fd);
		return -1;
	}

	user_buf = calloc(reqbuf.count,sizeof(*user_buf));
	if(user_buf == NULL){
		err("faill calloc user_buf\n");
		close(fd);
		return -1;
	}

	for(i = 0;i < reqbuf.count;i ++){
		memzero(v4l2_buf);
		v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		v4l2_buf.memory = V4L2_MEMORY_MMAP;
		v4l2_buf.index = i;

		if(ioctl(fd, VIDIOC_QUERYBUF, &v4l2_buf) < 0){
			err("Fail to query buf");
			close(fd);
			return -1;
		}

		user_buf[i].length = v4l2_buf.length;
		user_buf[i].start =	mmap(
					NULL,
					v4l2_buf.length,
					PROT_READ | PROT_WRITE,
					MAP_SHARED,
					fd, v4l2_buf.m.offset
				);

		if(MAP_FAILED == user_buf[i].start){
			err("Fail to mmap\n");
			close(fd);
			return -1;
		}

		if(ioctl(fd, VIDIOC_QBUF, &v4l2_buf) < 0){
			err("Fail to queue buf");
			close(fd);
			return -1;
		}
	}

  	memzero(stream);
	stream.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	stream.parm.capture.capturemode = 0;
	stream.parm.capture.timeperframe.numerator = 1;
	stream.parm.capture.timeperframe.denominator = option.rate;
	if(ioctl(fd, VIDIOC_S_PARM, &stream) < 0){
		close(fd);
		err("Failed to set param");
	}
	memzero(stream);
	stream.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if(ioctl(fd, VIDIOC_G_PARM, &stream) < 0){
		close(fd);
		err("Failed to set param");
	}
	
	v4l2_buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if(-1 == ioctl(fd, VIDIOC_STREAMON, &v4l2_buf_type)){
		err("Fail to ioctl stream on");
		close(fd);
		return -1;
	}

	pthread_create(&camera_process_tid, NULL, camera_process_thread,(void *)NULL);

	info("camera init successfully");
	return 0;
}

int camera_exit(struct options option)
{
	enum v4l2_buf_type type;
	int i;

	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if(ioctl(cam_fd, VIDIOC_STREAMOFF, &type) < 0){
		err("Fail to stream off");
		return -1;
	}

	for(i = 0;i < ReqButNum;i ++){
		if(munmap(user_buf[i].start, user_buf[i].length) < 0)	{
			return -1;
		}
	}
	free(user_buf);
	close(cam_fd);
	info("camera encode close");
	return 0;
}
